#include<cstdio>
#include<cstring>
#include<string>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <vector>

#define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
            __LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)

template<class T>
void mem_write(T address,T value){
    void *map_base,*virt_addr;
    char dev_name[] = "/dev/mem";
    int fd;
    unsigned page_size,mapped_size,offset_in_page;
    off_t target = address;

    fd  =  open(dev_name,O_RDWR|O_SYNC);

    if(fd < 0) FATAL;

    mapped_size = page_size = sysconf(_SC_PAGESIZE);

    offset_in_page = (unsigned)target & (page_size -1);

    if((offset_in_page + 32) > page_size){
        mapped_size *=2;
    }

    map_base = mmap(NULL,mapped_size,PROT_READ|PROT_WRITE, MAP_SHARED,fd,target &~(off_t)(page_size-1));

    virt_addr = (char*)map_base + offset_in_page;

    if(virt_addr == (void*)-1) FATAL;

    *(volatile T*)virt_addr = value;

    if(munmap(map_base,mapped_size) == -1) FATAL;
    close(fd);
}

template<class T>
void mem_read(T address,T& value){
    void *map_base,*virt_addr;
    char dev_name[] = "/dev/mem";
    int fd;
    unsigned page_size,mapped_size,offset_in_page;
    off_t target = address;

    fd  =  open(dev_name,O_RDWR|O_SYNC);

    if(fd < 0) FATAL;

    mapped_size = page_size = sysconf(_SC_PAGESIZE);

    offset_in_page = (unsigned)target & (page_size -1);

    if((offset_in_page + 32) > page_size){
        mapped_size *=2;
    }

    map_base = mmap(NULL,mapped_size,PROT_READ|PROT_WRITE, MAP_SHARED,fd,target &~(off_t)(page_size-1));

    virt_addr = (char*)map_base + offset_in_page;

    if(virt_addr == (void*)-1) FATAL;

    value = *(volatile T*)virt_addr;

    if(munmap(map_base,mapped_size) == -1) FATAL;
    close(fd);
}

template<class T>
void mem_write(std::vector<std::pair<T,T>> w){
    for(int i = 0 ; i < w.size(); i++){
        mem_write<T>(w[i].first,w[i].second);
    }
}

template<class T>
void mem_read(std::vector<std::pair<T,T&>> r){
    for(int i = 0 ; i < r.size(); i++){
        mem_read<T>(r[i].first,r[i].second);
    }
}

template<class T>
T *phy_mem_alloc(int32_t& fd,uint32_t phyaddr, int32_t& size) {
    void *map_base, *virt_addr;
    char dev_name[] = "/dev/mem";
    unsigned page_size, mapped_size, offset_in_page;
    off_t target = phyaddr;

    fd = open(dev_name, O_RDWR | O_SYNC);

    if (fd < 0) FATAL;

    mapped_size = page_size = sysconf(_SC_PAGESIZE);
    mapped_size += size; //允许访问的空间的大小,允许使用的内存空间的大小

    offset_in_page = (unsigned) target & (page_size - 1);

    if ((offset_in_page + 32) > page_size) {
        mapped_size *= 2;
    }

    mapped_size = size; //将实际的长度同时进行返回使用
    map_base = mmap(NULL, mapped_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~(off_t) (page_size - 1));

    virt_addr = (char *) map_base + offset_in_page;

    if (virt_addr == (void *) -1) FATAL;
    return (T*)virt_addr;
}

template<class T>
void phy_mem_free(T *mem, int32_t size,int fd) {
    if (munmap(mem, size) == -1)
        printf("phy mem free error: %s\n", strerror(errno));
    close(fd);
}
